///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef _ResourceGroupManager_H__
#define _ResourceGroupManager_H__

#include "OgrePrerequisites.h"
#include "OgreSingleton.h"
#include "OgreCommon.h"
#include "OgreDataStream.h"
#include "OgreResource.h"
#include "OgreArchive.h"
#include "OgreIteratorWrappers.h"
#include <ctime>

// If X11/Xlib.h gets included before this header (for example it happens when
// including wxWidgets and FLTK), Status is defined as an int which we don't
// want as we have an enum named Status.
#ifdef Status
#undef Status
#endif

namespace Ogre {

	
	
	
    class _OgreExport ResourceGroupListener
    {
    public:
        virtual ~ResourceGroupListener() {}

		
		virtual void resourceGroupScriptingStarted(const String& groupName, size_t scriptCount) = 0;
		
		virtual void scriptParseStarted(const String& scriptName, bool& skipThisScript) = 0;

		
		virtual void scriptParseEnded(const String& scriptName, bool skipped) = 0;
		
		virtual void resourceGroupScriptingEnded(const String& groupName) = 0;

		
		virtual void resourceGroupPrepareStarted(const String& groupName, size_t resourceCount)
                { (void)groupName; (void)resourceCount; }

		
		virtual void resourcePrepareStarted(const ResourcePtr& resource)
                { (void)resource; }

        
        virtual void resourcePrepareEnded(void) {}
        
        virtual void worldGeometryPrepareStageStarted(const String& description)
        { (void)description; }

        
        virtual void worldGeometryPrepareStageEnded(void) {}
        
        virtual void resourceGroupPrepareEnded(const String& groupName)
        { (void)groupName; }

		
		virtual void resourceGroupLoadStarted(const String& groupName, size_t resourceCount) = 0;
		
		virtual void resourceLoadStarted(const ResourcePtr& resource) = 0;
        
        virtual void resourceLoadEnded(void) = 0;
        
        virtual void worldGeometryStageStarted(const String& description) = 0;
        
        virtual void worldGeometryStageEnded(void) = 0;
        
        virtual void resourceGroupLoadEnded(const String& groupName) = 0;
    };

	/**
	 @remarks	This class allows users to override resource loading behavior.
				By overriding this class' methods, you can change how resources
				are loaded and the behavior for resource name collisions.
	*/
	class ResourceLoadingListener
	{
	public:
		virtual ~ResourceLoadingListener() {}

		
		virtual DataStreamPtr resourceLoading(const String &name, const String &group, Resource *resource) = 0;

		
		virtual void resourceStreamOpened(const String &name, const String &group, Resource *resource, DataStreamPtr& dataStream) = 0;

		
		virtual bool resourceCollision(Resource *resource, ResourceManager *resourceManager) = 0;
	};

    
    class _OgreExport ResourceGroupManager : public Singleton<ResourceGroupManager>, public MemAlloc_Resource
    {
    public:
		OGRE_AUTO_MUTEX // public to allow external locking
		/// Default resource group name
		static String DEFAULT_RESOURCE_GROUP_NAME;
        /// Internal resource group name (should be used by OGRE internal only)
        static String INTERNAL_RESOURCE_GROUP_NAME;
		/// Special resource group name which causes resource group to be automatically determined based on searching for the resource in all groups.
		static String AUTODETECT_RESOURCE_GROUP_NAME;
		/// The number of reference counts held per resource by the resource system
		static size_t RESOURCE_SYSTEM_NUM_REFERENCE_COUNTS;
        /// Nested struct defining a resource declaration
        struct ResourceDeclaration
        {
            String resourceName;
            String resourceType;
            ManualResourceLoader* loader;
			NameValuePairList parameters;
        };
        /// List of resource declarations
        typedef list<ResourceDeclaration>::type ResourceDeclarationList;
		typedef map<String, ResourceManager*>::type ResourceManagerMap;
		typedef MapIterator<ResourceManagerMap> ResourceManagerIterator;
		/// Resource location entry
		struct ResourceLocation
		{
			/// Pointer to the archive which is the destination
			Archive* archive;
			/// Whether this location was added recursively
			bool recursive;
		};
		/// List of possible file locations
		typedef list<ResourceLocation*>::type LocationList;

    protected:
		/// Map of resource types (strings) to ResourceManagers, used to notify them to load / unload group contents
        ResourceManagerMap mResourceManagerMap;

		/// Map of loading order (Real) to ScriptLoader, used to order script parsing
		typedef multimap<Real, ScriptLoader*>::type ScriptLoaderOrderMap;
		ScriptLoaderOrderMap mScriptLoaderOrderMap;

		typedef vector<ResourceGroupListener*>::type ResourceGroupListenerList;
        ResourceGroupListenerList mResourceGroupListenerList;

		ResourceLoadingListener *mLoadingListener;

        /// Resource index entry, resourcename->location 
        typedef map<String, Archive*>::type ResourceLocationIndex;

		/// List of resources which can be loaded / unloaded
		typedef list<ResourcePtr>::type LoadUnloadResourceList;
		/// Resource group entry
		struct ResourceGroup
		{
			enum Status
			{
				UNINITIALSED = 0,
				INITIALISING = 1,
				INITIALISED = 2,
				LOADING = 3,
				LOADED = 4
			};
			/// General mutex for dealing with group content
			OGRE_AUTO_MUTEX
			/// Status-specific mutex, separate from content-changing mutex
			OGRE_MUTEX(statusMutex)
			/// Group name
			String name;
			/// Group status
			Status groupStatus;
			/// List of possible locations to search
			LocationList locationList;
			/// Index of resource names to locations, built for speedy access (case sensitive archives)
			ResourceLocationIndex resourceIndexCaseSensitive;
            /// Index of resource names to locations, built for speedy access (case insensitive archives)
            ResourceLocationIndex resourceIndexCaseInsensitive;
			/// Pre-declared resources, ready to be created
			ResourceDeclarationList resourceDeclarations;
			/// Created resources which are ready to be loaded / unloaded
			// Group by loading order of the type (defined by ResourceManager)
			// (e.g. skeletons and materials before meshes)
			typedef map<Real, LoadUnloadResourceList*>::type LoadResourceOrderMap;
			LoadResourceOrderMap loadResourceOrderMap;
            /// Linked world geometry, as passed to setWorldGeometry
            String worldGeometry;
            /// Scene manager to use with linked world geometry
            SceneManager* worldGeometrySceneManager;
			// in global pool flag - if true the resource will be loaded even a different	group was requested in the load method as a parameter.
			bool inGlobalPool;

			void addToIndex(const String& filename, Archive* arch);
			void removeFromIndex(const String& filename, Archive* arch);
			void removeFromIndex(Archive* arch);

		};
        /// Map from resource group names to groups
        typedef map<String, ResourceGroup*>::type ResourceGroupMap;
        ResourceGroupMap mResourceGroupMap;

        /// Group name for world resources
        String mWorldGroupName;

		
		void parseResourceGroupScripts(ResourceGroup* grp);
		
		void createDeclaredResources(ResourceGroup* grp);
		
		void addCreatedResource(ResourcePtr& res, ResourceGroup& group);
		
		ResourceGroup* getResourceGroup(const String& name);
		
		void dropGroupContents(ResourceGroup* grp);
		
		void deleteGroup(ResourceGroup* grp);
		/// Internal find method for auto groups
		ResourceGroup* findGroupContainingResourceImpl(const String& filename);
		/// Internal event firing method
		void fireResourceGroupScriptingStarted(const String& groupName, size_t scriptCount);
		/// Internal event firing method
		void fireScriptStarted(const String& scriptName, bool &skipScript);
        /// Internal event firing method
        void fireScriptEnded(const String& scriptName, bool skipped);
		/// Internal event firing method
		void fireResourceGroupScriptingEnded(const String& groupName);
        /// Internal event firing method
		void fireResourceGroupLoadStarted(const String& groupName, size_t resourceCount);
        /// Internal event firing method
        void fireResourceLoadStarted(const ResourcePtr& resource);
		/// Internal event firing method
		void fireResourceLoadEnded(void);
		/// Internal event firing method
		void fireResourceGroupLoadEnded(const String& groupName);
        /// Internal event firing method
		void fireResourceGroupPrepareStarted(const String& groupName, size_t resourceCount);
        /// Internal event firing method
        void fireResourcePrepareStarted(const ResourcePtr& resource);
		/// Internal event firing method
		void fireResourcePrepareEnded(void);
		/// Internal event firing method
		void fireResourceGroupPrepareEnded(const String& groupName);

		/// Stored current group - optimisation for when bulk loading a group
		ResourceGroup* mCurrentGroup;
    public:
        ResourceGroupManager();
        virtual ~ResourceGroupManager();

        
        void createResourceGroup(const String& name, const bool inGlobalPool = true);


        
		void initialiseResourceGroup(const String& name);

		
		void initialiseAllResourceGroups(void);

		
        void prepareResourceGroup(const String& name, bool prepareMainResources = true, 
			bool prepareWorldGeom = true);

		
        void loadResourceGroup(const String& name, bool loadMainResources = true, 
			bool loadWorldGeom = true);

        
        void unloadResourceGroup(const String& name, bool reloadableOnly = true);

		
		void unloadUnreferencedResourcesInGroup(const String& name, 
			bool reloadableOnly = true);

		
		void clearResourceGroup(const String& name);
        
        
        void destroyResourceGroup(const String& name);

		
		bool isResourceGroupInitialised(const String& name);

		
		bool isResourceGroupLoaded(const String& name);

		/*** Verify if a resource group exists
		@param name The name of the resource group to look for
		*/
		bool resourceGroupExists(const String& name);

        
        void addResourceLocation(const String& name, const String& locType, 
            const String& resGroup = DEFAULT_RESOURCE_GROUP_NAME, bool recursive = false);
         
        void removeResourceLocation(const String& name, 
			const String& resGroup = DEFAULT_RESOURCE_GROUP_NAME);
         
		bool resourceLocationExists(const String& name, 
			const String& resGroup = DEFAULT_RESOURCE_GROUP_NAME);

        
        void declareResource(const String& name, const String& resourceType,
            const String& groupName = DEFAULT_RESOURCE_GROUP_NAME,
			const NameValuePairList& loadParameters = NameValuePairList());
        
        void declareResource(const String& name, const String& resourceType,
            const String& groupName, ManualResourceLoader* loader,
            const NameValuePairList& loadParameters = NameValuePairList());
        
        void undeclareResource(const String& name, const String& groupName);

		
		DataStreamPtr openResource(const String& resourceName, 
			const String& groupName = DEFAULT_RESOURCE_GROUP_NAME,
			bool searchGroupsIfNotFound = true, Resource* resourceBeingLoaded = 0);

		
		DataStreamListPtr openResources(const String& pattern, 
			const String& groupName = DEFAULT_RESOURCE_GROUP_NAME);
		
        
        StringVectorPtr listResourceNames(const String& groupName, bool dirs = false);

        
        FileInfoListPtr listResourceFileInfo(const String& groupName, bool dirs = false);

        
        StringVectorPtr findResourceNames(const String& groupName, const String& pattern,
            bool dirs = false);

        
        bool resourceExists(const String& group, const String& filename);

        
        bool resourceExists(ResourceGroup* group, const String& filename);
		
        
        bool resourceExistsInAnyGroup(const String& filename);

		
		const String& findGroupContainingResource(const String& filename);

        
        FileInfoListPtr findResourceFileInfo(const String& group, const String& pattern,
            bool dirs = false);

		
		time_t resourceModifiedTime(const String& group, const String& filename); 
        
        StringVectorPtr listResourceLocations(const String& groupName);

        
        StringVectorPtr findResourceLocation(const String& groupName, const String& pattern);

		
		time_t resourceModifiedTime(ResourceGroup* group, const String& filename); 

		
		DataStreamPtr createResource(const String& filename, const String& groupName = DEFAULT_RESOURCE_GROUP_NAME, 
			bool overwrite = false, const String& locationPattern = StringUtil::BLANK);

		
		void deleteResource(const String& filename, const String& groupName = DEFAULT_RESOURCE_GROUP_NAME, 
			const String& locationPattern = StringUtil::BLANK);

		
		void deleteMatchingResources(const String& filePattern, const String& groupName = DEFAULT_RESOURCE_GROUP_NAME, 
			const String& locationPattern = StringUtil::BLANK);

		
        void addResourceGroupListener(ResourceGroupListener* l);
        
        void removeResourceGroupListener(ResourceGroupListener* l);

        
        void setWorldResourceGroupName(const String& groupName) {mWorldGroupName = groupName;}

        /// Gets the resource group that 'world' resources will use.
        const String& getWorldResourceGroupName(void) const { return mWorldGroupName; }

        
        void linkWorldGeometryToResourceGroup(const String& group, 
            const String& worldGeometry, SceneManager* sceneManager);

        
        void unlinkWorldGeometryFromResourceGroup(const String& group);

			
		bool isResourceGroupInGlobalPool(const String& name);

        
        void shutdownAll(void);


        
        void _registerResourceManager(const String& resourceType, ResourceManager* rm);

        
        void _unregisterResourceManager(const String& resourceType);

		
		ResourceManagerIterator getResourceManagerIterator()
		{ return ResourceManagerIterator(
			mResourceManagerMap.begin(), mResourceManagerMap.end()); }

        
        void _registerScriptLoader(ScriptLoader* su);

        
        void _unregisterScriptLoader(ScriptLoader* su);

		
		ScriptLoader *_findScriptLoader(const String &pattern);

		
		ResourceManager* _getResourceManager(const String& resourceType);

		
		void _notifyResourceCreated(ResourcePtr& res);

		
		void _notifyResourceRemoved(ResourcePtr& res);

		
		void _notifyResourceGroupChanged(const String& oldGroup, Resource* res);

		
		void _notifyAllResourcesRemoved(ResourceManager* manager);

        
        void _notifyWorldGeometryStageStarted(const String& description);
        
        void _notifyWorldGeometryStageEnded(void);

		
		StringVector getResourceGroups(void);
		
		ResourceDeclarationList getResourceDeclarationList(const String& groupName);

				
		const LocationList& getResourceLocationList(const String& groupName);

		/// Sets a new loading listener
		void setLoadingListener(ResourceLoadingListener *listener);
		/// Returns the current loading listener
		ResourceLoadingListener *getLoadingListener();

		
        static ResourceGroupManager& getSingleton(void);
        
        static ResourceGroupManager* getSingletonPtr(void);

    };
	
	
}

#endif
